package com.demarc.pesquisas.infraestrutura.util.relatorio;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;

import net.sf.jasperreports.engine.JRDataSource;
import net.sf.jasperreports.engine.JREmptyDataSource;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRExporterParameter;
import net.sf.jasperreports.engine.JRParameter;
import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;
import net.sf.jasperreports.engine.export.JRPdfExporter;
import net.sf.jasperreports.engine.export.JRPdfExporterParameter;

import org.apache.log4j.Logger;

import ar.com.fdvs.dj.core.DynamicJasperHelper;
import ar.com.fdvs.dj.core.layout.ClassicLayoutManager;
import ar.com.fdvs.dj.core.layout.HorizontalBandAlignment;
import ar.com.fdvs.dj.domain.AutoText;
import ar.com.fdvs.dj.domain.DynamicReport;
import ar.com.fdvs.dj.domain.Style;
import ar.com.fdvs.dj.domain.builders.ColumnBuilder;
import ar.com.fdvs.dj.domain.builders.DynamicReportBuilder;
import ar.com.fdvs.dj.domain.constants.Border;
import ar.com.fdvs.dj.domain.constants.Font;
import ar.com.fdvs.dj.domain.constants.HorizontalAlign;
import ar.com.fdvs.dj.domain.constants.Page;
import ar.com.fdvs.dj.domain.constants.VerticalAlign;
import ar.com.fdvs.dj.domain.entities.columns.AbstractColumn;

import com.demarc.pesquisas.infraestrutura.excecao.InfraException;

public class PDFReportHelper {

    private static final Logger LOGGER = Logger.getLogger(PDFReportHelper.class);

    public static byte[] toPDF(Report report, String templateLocation) {

        // converting the report to instances of DTOs
        List<ReportDTOForPDF> dtos = getReportDTOToPDF(report);

        // defining the font styles
        Font fontBold = new Font(report.getFontSize(), "DejaVu Sans", true, false, false);
        Font fontNormal = new Font(report.getFontSize(), "DejaVu Sans", false, false, false);
        Font fontFooterBold =
                new Font(report.getFontFooterSize(), "DejaVu Sans", false, false, false);

        Style titleStyle = new Style("titleStyle");
        titleStyle.setFont(fontBold);
        titleStyle.setHorizontalAlign(HorizontalAlign.CENTER);

        Style autoTextStyle = new Style();
        autoTextStyle.setFont(fontBold);

        Style headerStyle = new Style();
        headerStyle.setFont(fontBold);

        Style footerStyle = new Style();
        footerStyle.setFont(fontFooterBold);

        headerStyle.setHorizontalAlign(HorizontalAlign.CENTER);
        headerStyle.setStretchWithOverflow(true);
        headerStyle.setBorderBottom(Border.THIN());

        Style normalLeftStyle = createPdfStyle(fontNormal, HorizontalAlign.LEFT);
        Style normalCenterStyle = createPdfStyle(fontNormal, HorizontalAlign.CENTER);
        Style normalRightStyle = createPdfStyle(fontNormal, HorizontalAlign.RIGHT);

        AutoText footer =
                new AutoText(report.getFooter(), AutoText.POSITION_FOOTER,
                        HorizontalBandAlignment.LEFT);
        footer.setStyle(footerStyle);

        DynamicReportBuilder builder = new DynamicReportBuilder();
        if (report.isLandscape()) {
            builder.setPageSizeAndOrientation(Page.Page_A4_Landscape());
        }

        builder.addAutoText(footer);
        builder.setTitle(report.getTitle());
        builder.setDetailHeight(15);
        builder.setMargins(30, 20, 30, 15);
        builder.setColumnsPerPage(1);
        builder.setDefaultStyles(titleStyle, null, headerStyle, normalCenterStyle);
        builder.setTemplateFile(templateLocation);

        // writing the headers and the columns properties
        ByteArrayOutputStream output = null;
        try {
            int i = 1;

            for (ReportHeader header : report.getHeaders()) {
                ColumnBuilder columnBuilder =
                        ColumnBuilder.getNew()
                                .setColumnProperty("value" + i++, String.class.getName())
                                .setTitle(header.getAsString()).setWidth(header.getWidth());

                columnBuilder.setStyle(getStyle(header, normalLeftStyle, normalRightStyle,
                        normalCenterStyle));
                AbstractColumn column = columnBuilder.build();
                builder.addColumn(column);
            }
            builder.setUseFullPageWidth(true);

            DynamicReport dr = builder.build();
            output = new ByteArrayOutputStream();
            JRPdfExporter exporterPDF = createPdfExporter(dr, output, dtos);
            exporterPDF.exportReport();
            return output.toByteArray();
        } catch (Exception e) {
            throw new InfraException(e);
        } finally {
            if (output != null) {
                try {
                    output.close();
                } catch (IOException e) {
                    LOGGER.error(e.getMessage());
                }
            }
        }
    }

    private static Style getStyle(ReportHeader header, Style normalLeftStyle,
            Style normalRightStyle, Style normalCenterStyle) {
        Style style;

        if (ReportColumnHorizontalAlignment.LEFT.equals(header.getColumnAlignment())) {
            style = normalLeftStyle;
        } else if (ReportColumnHorizontalAlignment.RIGHT.equals(header.getColumnAlignment())) {
            style = normalRightStyle;
        } else {
            style = normalCenterStyle;
        }
        return style;
    }

    private static JRPdfExporter createPdfExporter(DynamicReport dr, ByteArrayOutputStream output,
            List<ReportDTOForPDF> dtos) throws JRException {
        JRDataSource ds = getReportDataSource(dtos);

        JasperPrint jasperPrint =
                DynamicJasperHelper.generateJasperPrint(dr, new ClassicLayoutManager(), ds);

        JRPdfExporter exporterPDF = new JRPdfExporter();
        exporterPDF.setParameter(JRPdfExporterParameter.JASPER_PRINT, jasperPrint);
        exporterPDF.setParameter(JRPdfExporterParameter.OUTPUT_STREAM, output);
        exporterPDF.setParameter(JRPdfExporterParameter.IS_CREATING_BATCH_MODE_BOOKMARKS,
                Boolean.TRUE);
        exporterPDF.setParameter(JRPdfExporterParameter.IGNORE_PAGE_MARGINS, Boolean.TRUE);
        exporterPDF.setParameter(JRPdfExporterParameter.FORCE_LINEBREAK_POLICY, Boolean.TRUE);
        return exporterPDF;
    }

    private static List<ReportDTOForPDF> getReportDTOToPDF(Report report) {
        List<ReportDTOForPDF> dtos = new ArrayList<ReportDTOForPDF>();

        for (ReportRow row : report.getRows()) {
            ReportDTOForPDF dto = new ReportDTOForPDF();
            int i = 1;
            for (ReportColumn column : row.getColumns()) {
                dto.addValue(i++, column.getAsString());
            }
            dtos.add(dto);
        }
        return dtos;
    }

    private static Style createPdfStyle(Font fontNormal, HorizontalAlign horizontalAlign) {
        Style style = new Style();
        style.setFont(fontNormal);
        style.setVerticalAlign(VerticalAlign.TOP);
        style.setHorizontalAlign(horizontalAlign);
        style.setStretchWithOverflow(true);
        style.setBorderBottom(Border.THIN());
        return style;
    }

    public static ByteArrayOutputStream exportReportToPdfOutputStream(String reportData,
            List<?> data, Map<String, Object> reportParameters, String title) {

        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        try {
            JasperPrint print =
                    JasperFillManager.fillReport(reportData, reportParameters,
                            getReportDataSource(data));

            JRPdfExporter exporter = new JRPdfExporter();
            exporter.setParameter(JRPdfExporterParameter.METADATA_TITLE, title);

            ResourceBundle reportBundle =
                    (ResourceBundle) reportParameters.get(JRParameter.REPORT_RESOURCE_BUNDLE);
            if (reportBundle != null) {
                exporter.setParameter(JRPdfExporterParameter.METADATA_AUTHOR,
                        reportBundle.getString("label_report_author"));
            }
            exporter.setParameter(JRExporterParameter.JASPER_PRINT, print);
            exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, outputStream);
            exporter.setParameter(JRPdfExporterParameter.IS_COMPRESSED, Boolean.TRUE);
            exporter.exportReport();
        } catch (JRException e) {
            throw new InfraException("Could not export report to PDF OutputSream.", e);
        }
        return outputStream;
    }

    private static JRDataSource getReportDataSource(List<?> data) {

        JRDataSource dataSource;
        if (data == null) {
            dataSource = new JREmptyDataSource();
        } else {
            dataSource = new JRBeanCollectionDataSource(data);
        }
        return dataSource;
    }
}